<?php

namespace App\Providers;

use Laravel\Horizon\Horizon;
use Illuminate\Routing\Route;
use Illuminate\Support\Carbon;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
use Illuminate\Database\Events\QueryExecuted;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot()
    {
        Carbon::setLocale('zh');

        $this->extendBlades();
        // 自定义验证器
        $this->extendValidators();
        // 时间友好化
        $this->registerCarbonMacros();
        // 路由拓展
        $this->registerRouteMacros();
        // SQL记录
        $this->registerDbEvents();
        // 抽空改到中間件 horizon.middleware
        Horizon::auth(function ($request) {

            return true;
        });
    }

    /**
     * Register
     */
    public function register()
    {
    }

    public function registerDbEvents()
    {
        if (in_array(config('app.env'), ['dev', 'debug']) && config('app.debug')) {
            DB::listen(function (QueryExecuted $query) {
                $sqlWithPlaceholders = str_replace(['%', '?'], ['%%', '%s'], $query->sql);

                $bindings = $query->connection->prepareBindings($query->bindings);
                $pdo      = $query->connection->getPdo();
                $realSql  = vsprintf($sqlWithPlaceholders, array_map([$pdo, 'quote'], $bindings));
                $duration = $this->formatDuration($query->time / 1000);

                info(sprintf('[%s] %s | %s: %s', $duration, $realSql, request()->method(), request()->getRequestUri()));
            });
        }
    }

    private function formatDuration($seconds)
    {
        if ($seconds < 0.001) {
            return round($seconds * 1000000) . 'μs';
        } elseif ($seconds < 1) {
            return round($seconds * 1000, 2) . 'ms';
        }

        return round($seconds, 2) . 's';
    }

    public function registerCarbonMacros()
    {
        if (!Carbon::hasMacro('toHumanString')) {
            Carbon::macro('toHumanString', function () {

                if ($this->isCurrentMinute()) {
                    return "剛剛";
                }

                if ($this->isCurrentHour()) {
                    return $this->diffInMinutes() . "分鐘前";
                }

                if ($this->isToday()) {
                    return "今天 " . $this->format('H:i');
                }

                if ($this->isYesterday()) {
                    return "昨天 " . $this->format('H:i');
                }

                return $this->format('m-d H:i');
            });
        }
    }

    public function registerRouteMacros()
    {
        if (!Route::hasMacro('menu')) {
            Route::macro('menu', function ($name) {
                $this->action['menu'] = $name;
                return $this;
            });
        }

        if (!Route::hasMacro('getActionMenu')) {
            Route::macro('getActionMenu', function () {
                return $this->action['menu'] ?? '';
            });
        }

        if (!Route::hasMacro('description')) {
            Route::macro('description', function ($description) {
                $this->action['description'] = $description;
                return $this;
            });
        }

        if (!Route::hasMacro('getActionDescription')) {
            Route::macro('getActionDescription', function () {
                return $this->action['description'] ?? '';
            });
        }
    }

    // blade extends
    private function extendBlades()
    {
        // @setting
        Blade::directive('setting', function ($expression) {
            return "<?php echo setting($expression); ?>";
        });
        // @notempty
        Blade::directive('notempty', function ($expression) {
            return "<?php if (!empty($expression)): ?>";
        });
        Blade::directive('endnotempty', function ($expression) {
            return "<?php endif; ?>";
        });
        // @route
        Blade::directive('route', function ($expression) {
            return "<?php echo route($expression); ?>";
        });
        // @broute
        Blade::directive('broute', function ($expression) {
            return "<?php echo broute($expression); ?>";
        });
        // @in
        Blade::directive('in', function ($expression) {
            return "<?php if (inArray($expression)): ?>";
        });
        Blade::directive('endin', function ($expression) {
            return "<?php endif; ?>";
        });
        // @notin
        Blade::directive('notin', function ($expression) {
            return "<?php if (!inArray($expression)): ?>";
        });
        Blade::directive('endnotin', function ($expression) {
            return "<?php endif; ?>";
        });
    }

    // validator extends
    private function extendValidators()
    {
        collect(config('regex'))->each(function ($regex, $rule) {
            if (!$regex) {
                return;
            }

            Validator::extend($rule, function ($attribute, $value, $parameters, $validator) use ($regex) {
                return preg_match($regex, $value) ? true : false;
            });
        });
        Validator::extend('password', function ($attribute, $value, $parameters, $validator) {
            $chars  = '/[' . strtr(preg_quote('`~!@#$%^&()_+-*/={}[]\|;:\'",.<>/?'), ['/' => '\\/']) . ']/';
            $letter = "/[a-zA-Z]/";
            $number = "/[0-9]/";

            $matches = 0;
            foreach (compact('chars', 'letter', 'number') as $regex) {
                if (preg_match($regex, $value)) {
                    $matches++;
                }
            }

            return $matches >= 2 ? true : false;
        });

        // 包含中文的總字符數區間
        Validator::extend('charlength', function ($attribute, $value, $parameters, $validator) {
            $min = $parameters[0];
            $max = $parameters[1];
            if (mb_strlen($value) < $min || mb_strlen($value) > $max) {
                return false;
            }
            return true;
        });

        // 驗證是否是合法json數據
        Validator::extend('json', function ($attribute, $value, $parameters, $validator) {
            json_decode($value);
            return (json_last_error() == JSON_ERROR_NONE);
        });
    }
}
